package cn.qu.v1.platform.inject.factory;

import cn.qu.v1.platform.aop.MethodProxyInvoker;
import cn.qu.v1.platform.aop.ProxyMethod;
import cn.qu.v1.platform.inject.BeanKey;
import cn.qu.v1.platform.inject.MethodBeanDef;
import cn.qu.v1.platform.inject.ProxyBeanDef;
import cn.qu.v1.platform.inject.annotation.Qualifier;
import com.google.common.base.Strings;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.commons.lang3.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by zh on 17/1/17.
 */
public class ProxyFactoryBean<T> extends PluginFactoryBean<T> {
    private List<ProxyMethod> proxyMethodList;

    private T obj;

    public ProxyFactoryBean(String bucketName, ClassLoader classLoader, BeanKey beanKey, MethodBeanDef methodBeanDef, List<ProxyMethod> proxyMethodList) {
        super(bucketName, classLoader, beanKey, methodBeanDef);
        this.proxyMethodList = proxyMethodList;
    }


    private T createBean() throws Exception {
        Method method = getMethodBeanDef().getMethod();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(method.getReturnType());
        enhancer.setCallback(new ProxyCallback());
        enhancer.setClassLoader(getClassLoader());

        T t = null;
        int count = method.getParameterCount();
        if (count > 0) {
            Class<?>[] paramTypes = method.getParameterTypes();
            Annotation[][] paramAnnos = method.getParameterAnnotations();

            List<Object> params = new ArrayList<>();
            for (int i = 0; i < paramAnnos.length; i++) {
                Annotation[] annos = paramAnnos[i];
                Class<?> type = paramTypes[i];
                Qualifier qualifier = (Qualifier) getParameterAnnotations(annos, Qualifier.class);

                String beanName = qualifier == null ? null : qualifier.value();
                String pluginName = qualifier == null || Strings.isNullOrEmpty(qualifier.pluginName()) ? getBucketName() : qualifier.pluginName();

                BeanKey key = BeanKey.getKey(beanName, type);

                PluginFactoryBean bean = super.getBeanContext().findFactoryBean(pluginName, key);
                params.add(bean.getObject());
            }

            t = (T) enhancer.create(paramTypes, params.toArray());
        } else {
            t = (T) enhancer.create();
        }
        if (this.isSingleton()) {
            obj = t;
        }
        return obj;
    }

    @Override
    public T getObject() throws Exception {
        if (obj == null) {
            return createBean();
        }
        return obj;
    }

    class ProxyCallback implements MethodInterceptor {

        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            List<ProxyBeanDef> defList = new ArrayList<>();

            for (ProxyMethod proxyMethod : proxyMethodList) {
                for (ProxyBeanDef def : proxyMethod.getAopProxy()) {

                    String key = def.getRegex();
                    if (key.startsWith("annotation:")) {
                        String className = StringUtils.removeStart(key, "annotation:");
                        Annotation[] annos = method.getAnnotations();
                        if (annos != null) {
                            for (Annotation anno : annos) {
                                if (StringUtils.equals(anno.annotationType().getName(), className)) {
                                    defList.add(def);
                                }
                            }
                        }
                    }
                }
            }

            if (defList.isEmpty()) {
                return methodProxy.invokeSuper(o, args);
            } else {
                return new MethodProxyInvoker(methodProxy, defList).invokeSuper(o, args);
            }
        }

    }
}
